package com.example.printlib;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;

import com.example.baseutils.Constance;
import com.hiti.usb.printer.PrinterJob;
import com.hiti.usb.printer.PrinterStatus;
import com.hiti.usb.service.ServiceConnector;
import com.hiti.usb.utility.FileUtility;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 打印服务
 */
public class PrintService extends Service {
    private static final String TAG = "PrintService";
    private final IBinder binder = new PrintBinder();
    private String m_strTablesRoot = "";
    private ServiceConnector serviceConnector;
    private PrinterOperation operation;
    private ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
    private ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

    private LinkedBlockingDeque<String> queue = new LinkedBlockingDeque<>();
    private boolean running = false;

    public void addPrintQueue(String path) {
        queue.add(path);
    }

    public enum PrintServerStatus {
        connecting,
        connected,
        disconnected,
        error,
        otherStatus,
        printStart,
        printing,
        printOk;
    }

    //默认连接状态为断开连接，四秒无响应立马变为断链状态
    private PrintServerStatus printerStatus = PrintServerStatus.connecting;
    private String printerDescription = "";
    private int disconnectTime = 0;

    private CopyOnWriteArrayList<PrintCallback> printCallbacks = new CopyOnWriteArrayList<>();

    public void addPrintCallback(PrintCallback callback) {
        if (!printCallbacks.contains(callback))
            printCallbacks.add(callback);
    }

    public void removePrintCallback(PrintCallback callback) {
        printerStatus= PrintServerStatus.connected;
        printCallbacks.remove(callback);
    }

    public PrintServerStatus getPrinterStatus() {
        return printerStatus;
    }

    public PrintService() {
    }

    private void initPrint() {
//        copyFileOrDir("Tables");
        running = true;
        m_strTablesRoot = Constance.INSTANCE.getROOT_DIR() + "/tables";
        serviceConnector = ServiceConnector.register(getApplicationContext(), new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Log.i(TAG, "onReceive: " + intent.getAction());
            }
        });
        operation = new PrinterOperation(this, serviceConnector);
        serviceConnector.StartService();
        executor.scheduleAtFixedRate(() -> {
            Log.i(TAG, "initPrint: " + serviceConnector.getHitiServiceStatus());
            try {
                doPrintStateJob();
                notifyPrinterStatus();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }, 2, 2, TimeUnit.SECONDS);

        cachedThreadPool.execute(() -> {
            while (running) {
                try {
                    Log.i(TAG, "queue.take before: ");
                    String path = queue.take();
                    Log.i(TAG, "path: " + path);
                    printerStatus = PrintServerStatus.printStart;
                    printerDescription = "start print";
                    PrinterJob job = operation.print(path);
                    PrinterStatus status = ((PrinterStatus) job.retData);
                    if (status != null) {
                        Log.i(TAG, "status: " + status.statusDescription + " getDescription " + PrinterStatus.getDescription(status.statusValue));
                    } else {
                        Log.e(TAG, "miss job");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private Handler mainHandler = new Handler(Looper.getMainLooper());
    private void notifyPrinterStatus() {
        Log.i(TAG, "notifyPrinterStatus: "+printerStatus);
        mainHandler.post(() -> {
            for (PrintCallback printCallback : printCallbacks) {
                printCallback.onPrintState(printerStatus, printerDescription);
            }
        });
    }
    //查看print打印机状态
    private void doPrintStateJob() {
        cachedThreadPool.execute(() -> {
            Log.i(TAG, "doPrintStateJob: ");
            PrinterJob job = operation.getPrinterStatus();
            PrinterStatus status = ((PrinterStatus) job.retData);
            if (status != null) {
                disconnectTime = 0;
                //如果之前为断链，现在连接上，则将状态改为已连接
                if (printerStatus == PrintServerStatus.disconnected)
                    printerStatus = PrintServerStatus.connected;
                String description = PrinterStatus.getDescription(status.statusValue);
                if (printerStatus != PrintServerStatus.printStart)
                    printerDescription = description;
                Log.i(TAG, "status: " + status.statusDescription + " getDescription " + printerDescription);
                if (printerDescription.contains("ready")
                        && (printerStatus == PrintServerStatus.printing||printerStatus==PrintServerStatus.printStart)) {
                    printerStatus = PrintServerStatus.printOk;
                    return;
                }
                if (printerDescription.contains("printing")) {
                    printerStatus = PrintServerStatus.printing;
                    return;
                }
                if (printerDescription.contains("error")) {
                    printerStatus = PrintServerStatus.error;
                    return;
                }
                if(!printerDescription.contains("ready"))
                    printerStatus = PrintServerStatus.otherStatus;

            } else {
                Log.e(TAG, "miss job");
                disconnectTime++;
                if (disconnectTime > 2) {
                    printerStatus = PrintServerStatus.disconnected;
                    printerDescription = "device disconnected";
                }
            }
        });
    }

    public class PrintBinder extends Binder {
        public PrintService getService() {
            return PrintService.this;
        }
    }

    public interface PrintCallback {
        void onPrintState(PrintServerStatus state, String description);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (!running)
            initPrint();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy: ");
        running = false;
        printCallbacks.clear();
        executor.shutdown();
        cachedThreadPool.shutdown();
        serviceConnector.unregister();
    }

    private void copyFileOrDir(String path) {
        //初始化打印
        if (!FileUtility.FileExist(m_strTablesRoot)) {
            FileUtility.CreateFolder(m_strTablesRoot);
        }
        AssetManager assetManager = this.getAssets();
        String assets[] = null;
        try {
            assets = assetManager.list(path);
            if (assets.length == 0) {
                copyFile(path);
            } else {
                String fullPath = "/data/data/" + this.getPackageName() + "/" + path;
                File dir = new File(fullPath);
                if (!dir.exists())
                    dir.mkdir();
                for (int i = 0; i < assets.length; ++i) {
                    copyFileOrDir(path + "/" + assets[i]);
                }
            }
        } catch (IOException ex) {
            Log.e("TAG", "I/O Exception", ex);
        }
    }

    private void copyFile(String filename) {
        AssetManager assetManager = this.getAssets();
        InputStream in = null;
        OutputStream out = null;
        try {
            Log.e(TAG, "filename: " + filename);
            in = assetManager.open(filename);
            String newFileName = Constance.INSTANCE.getROOT_DIR() + "/" + filename;
            out = new FileOutputStream(newFileName);
            byte[] buffer = new byte[1024];
            int read;
            while ((read = in.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
            in.close();
            in = null;
            out.flush();
            out.close();
            out = null;
        } catch (Exception e) {
            Log.e("TAG", e.getMessage());
        }

    }


}